#pragma once

#include <string>
#include <functional>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <vector>
#include <cstring>

#include "Utils.h"


namespace tool {
namespace Log {


class LogFormat
{
public:
    constexpr LogFormat(const char* fileName, int line, const char* funcName, int level, const char* time = nullptr)
        :m_file(fileName), m_line(line), m_function(funcName), m_level(level), m_time(time)
    {}

    static void SetFormatString(std::string format)
    {
        scFormat = std::move(format);
    }
    std::string GetPrefix() const;
public:
    static std::string scFormat;
    const char* m_file;
    int			m_line;
    const char* m_function;
    int			m_level;
    const char* m_time;
};



//输出流,包括一个write函数,一个flush函数，一个close函数
using LogFunctor = std::tuple<std::function<void(const char* data, int size)>, std::function<void()>, std::function<void()>>;


namespace detail
{

static constexpr int scSmallBuffer = 4 * 1024;//4KB
static constexpr int scLargeBuffer = 4 * 1024 * 1024;//4MB



template<int SIZE>
class FixedBuffer
{
    DISABLE_COPY(FixedBuffer)
public:
    FixedBuffer()
        : m_cur(m_data)
    {}

    ~FixedBuffer()
    {}

    void append(const char* /*restrict*/ buf, int len)
    {
        // FIXME: append partially
        if (avail() > len)
        {
            std::memcpy(m_cur, buf, len);
            m_cur += len;
        }
    }

    const char* data() const { return m_data; }

    int length() const { return static_cast<int>(m_cur - m_data); }

    // write to m_data directly
    char* current() { return m_cur; }

    int avail() const { return static_cast<int>(end() - m_cur); }

    void add(size_t len) { m_cur += len; }

    void reset() { m_cur = m_data; }

    void bzero() { std::memset(m_data, 0, sizeof(m_data)); }

private:
    const char* end() const { return m_data + sizeof m_data; }

    char m_data[SIZE];
    char* m_cur;
};

}  // namespace detail

/*!
 * @brief 单次打印上限为4MB
*/
class AsyncLog
{
    DISABLE_COPY(AsyncLog)
public:
    static AsyncLog* GetInstance()
    {
        static AsyncLog s_instance;
        return &s_instance;
    }

    ~AsyncLog();

    void Start(int interval_ms)
    {
        m_flushInterval_ms = interval_ms;
        m_running = true;
        m_writeThread = std::thread(&AsyncLog::WriteThread, this);
    }

    void Stop()
    {
        m_running = false;
        m_cv.notify_all();
        if (m_writeThread.joinable())
            m_writeThread.join();
    }

    void ShowMessage(const char* pattern, ...)  CHECK_FORMAT(2, 3);

    void ShowMessage(const LogFormat& format, const char* pattern, ...)  CHECK_FORMAT(3, 4);

    void setRollCondition(int recordTime_sec, int64_t recordSize_byte);

    void setScreenState(bool state);
    void setFileState(bool state, std::string fileName = "");

    void installMessageCallback(std::function<void(const char* data, int size)> writer,
        std::function<void()> flusher = nullptr,
        std::function<void()> closer = nullptr);

    void setColorState(bool state);
private:
    AsyncLog();

    void Append(const char* data, int size);

    void WriteThread();

    void DataWrite(const char* data, int size);
    void DataFlush();
    void DataClose();

    using Buffer = detail::FixedBuffer<detail::scLargeBuffer>;
    using BufferPtr = std::unique_ptr<Buffer>;
    using BufferVector = std::vector<BufferPtr>;
private:
    std::thread             m_writeThread;
    std::atomic<bool>       m_running = ATOMIC_VAR_INIT(false);
    int                     m_flushInterval_ms = 2000;
    int64_t                 m_rollTime_sec = 60 * 60 * 24;//1 day
    int64_t                 m_rollSize_byte = 1024 * 1024 * 1024;//1GB
    int64_t                 m_startRecordTime;

    std::mutex              m_mutex;
    std::condition_variable m_cv;
    BufferVector            m_vecBuf;
    BufferPtr               m_currBuf;
    BufferPtr               m_nextBuf;
    LogFunctor              m_toDefault;
    LogFunctor              m_toFile;
    LogFunctor              m_toInstaller;

    bool                    m_useColor = false;
};

}
}//namspace tool::Log
